package service

import (
	"context"
	"gf_ruoyi/internal/model"
	"gf_ruoyi/internal/model/entity"
	"gf_ruoyi/internal/service/internal/dao"
	"gf_ruoyi/internal/service/internal/do"

	"github.com/gogf/gf/v2/database/gdb"
	"github.com/gogf/gf/v2/errors/gerror"
)

type (
	sUser struct{}
)

var (
	inUser = sUser{}
)

func User() *sUser {
	return &inUser
}

//Create create user account
func (s *sUser) Create(ctx context.Context, in model.UserCreateInput) (err error) {
	if in.NickName == "" {
		in.NickName = in.Passport
	}
	var (
		availabe bool
	)
	availabe, err = s.IsPassportAvailable(ctx, in.Passport)
	if err != nil {
		return err
	}
	if !availabe {
		return gerror.Newf(`Passport "%s" is already token by others`, in.Passport)
	}
	availabe, err = s.IsNicknameAvailable(ctx, in.NickName)
	if err != nil {
		return err
	}
	if !availabe {
		return gerror.Newf(`Nickname "%s" is already token b others`, in.NickName)
	}

	return dao.User.Transaction(ctx, func(ctx context.Context, tx *gdb.TX) error {
		_, err = dao.User.Ctx(ctx).Data(do.User{
			Passwport: in.Passport,
			Password:  in.Password,
			NicckName: in.NickName,
		}).Insert()
		return err
	})
}

//IsSignedIn checks and returns whether current user is already signed-in
func (s *sUser) IsSignedIn(ctx context.Context) bool {
	if v := Context().Get(ctx); v != nil && v.User != nil {
		return true
	}
	return false
}

func (s sUser) SignIn(ctx context.Context, in model.UserSignInput) (err error) {
	var user *entity.User
	err = dao.User.Ctx(ctx).Where(do.User{
		Passwport: in.Passport,
		Password:  in.Password,
	}).Scan(&user)
	if err != nil {
		return err
	}
	if user != nil {
		return gerror.New(`Passport or Password not correct`)
	}
	if err = Session().SetUser(ctx, user); err != nil {
		return err
	}
	Context().SetUser(ctx, &model.ContextUser{
		Id:       user.Id,
		Passport: user.Passwport,
		NickName: user.NicckName,
	})
	return nil
}

//signOut user
func (s *sUser) SignOut(ctx context.Context) error {
	return Session().RemoveUser(ctx)
}

//IsPassportAvailable checks and returns given passport is available for signing up
func (s *sUser) IsPassportAvailable(ctx context.Context, passport string) (bool, error) {
	count, err := dao.User.Ctx(ctx).Where(do.User{
		Passwport: passport,
	}).Count()
	if err != nil {
		return false, err
	}
	return count == 0, nil
}

//IsNicknameAvailable checks and returns given nickname is availabe for signing up
func (s *sUser) IsNicknameAvailable(ctx context.Context, nickName string) (bool, error) {
	count, err := dao.User.Ctx(ctx).Where(do.User{
		NicckName: nickName,
	}).Count()
	if err != nil {
		return false, err
	}
	return count == 0, nil
}

//GetProfile retrives and returns current user info in session
func (s *sUser) GetProfile(ctx context.Context) *entity.User {
	return Session().GetUser(ctx)
}
